﻿#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stddef.h>
//宏
//offsetof - 计算结构体成员相较于起始位置的偏移量
//结构体的声明
struct stu {//变量名：1.字母、数字、下划线 2.不能是数字开头
	char name[20];
	int age;
	char sex[5];
	char id[20];
};
//struct stu {
//	char name[20];
//	int age;
//	char sex[5];
//	char id[20];
//}s1,s2,s3;

//匿名结构体类型 - 只能使用一次
//struct {
//	char name[20];
//	int age;
//	char sex[5];
//	char id[20];
//}x;

//自引用
// struct Node
//{
//	int data;
//	struct Node* next;
//};

//链表
//struct stu {
//	int data;//存放数据
//	struct Node* next;//存放下一个节点地址
//};

//重命名自引用
//typedef struct {//还没重命名就已经使用，错误的
//	  int data;
//    Node* next;
//}Node;
//自引用
//typedef struct Node {
//	int data;
//	struct Node* next;
//}Node;

//内存对齐（空间换时间）
struct S1 {
	char c1;//1 8    0-1
	char c2;//1 8    1-2
	int i;//4 8      4-7
};
struct S2 {
	char c1;//1 8    0-1
	int i;//  4 8    4-7
	char c2;//1 8    8-9
};
struct S3
{
	double d;//8 8   0-7
	char c;//  1 8   7-8
	int i;//   4 8   12-15
};
struct S4
{
	char c1;// 1 8   0-1   
	struct S3 s3;//8   8-23
	double d;//8 8   24-31
};
//对齐规则
//⾸先得掌握结构体的对⻬规则：
//1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
//2. 其他成员变量要对⻬到某个数字（对⻬数）的整数倍的地址处。
//对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
//- VS 中默认的值为 8
//-Linux中 gcc 没有默认对⻬数，对⻬数就是成员⾃⾝的⼤⼩
//3. 结构体总⼤⼩为最⼤对⻬数（结构体中每个成员变量都有⼀个对⻬数，所有对⻬数中最⼤的）的整数倍。
//4. 如果嵌套了结构体的情况，嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处，结构
//体的整体⼤⼩就是所有最⼤对⻬数（含嵌套结构体中成员的对⻬数）的整数倍。

//#pragma pack(1)//设置默认对⻬数为1
//struct S
//{
//	char c1;
//	int i;
//	char c2;
//};
//#pragma pack()//取消设置的对⻬数，还原为默认

struct S
{
	int data[1000];
	int num;
};
struct S s = { {1,2,3,4}, 1000 };
//结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);
}
//结构体地址传参
void print2(const struct S* ps)
{
	printf("%d\n", ps->num);
}
//⾸选print2函数
//因为：函数传参的时候，参数是需要压栈，会有时间和空间上的系统开销。
//如果传递⼀个结构体对象的时候，结构体过⼤，参数压栈的的系统开销⽐较⼤，所以会导致性能的下降。
//所以：结构体传参的时候，要传结构体的地址。

//位段(为了节省空间)
//1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型
//2. 位段的成员名后边有⼀个冒号和⼀个数字。
//3.位段的空间上是按照需要以4个字节（ int ）或者1个字节（ char ）的⽅式来开辟的。
//4.位段涉及很多不确定因素，位段是不跨平台的，注重可移植的程序应该避免使⽤位段。
struct A
{
	char a: 3;//数字代表字节（比特位）int不能超过32 char不能超过8
	char b: 4;//0 0000 000   00000000  00000000
	char c: 5;//0 1100 010   00000011  00000100
	char d: 4;//    b   a        c         d
};
// vs中是从右向左使用，如果剩余的空间不够下一个成员使用，就浪费
//位段的跨平台问题
//1. int 位段被当成有符号数还是⽆符号数是不确定的。
//2. 位段中最⼤位的数⽬不能确定。（16位机器最⼤16，32位机器最⼤32，写成27，在16位机器会出问题。
//3. 位段中的成员在内存中从左向右分配，还是从右向左分配标准尚未定义。
//4. 当⼀个结构包含两个位段，第⼆个位段成员⽐较⼤，⽆法容纳于第⼀个位段剩余的位时，是舍弃剩余的位还是利⽤，这是不确定的。
//跟结构相⽐，位段可以达到同样的效果，并且可以很好的节省空间，但是有跨平台的问题存在。
int main() {
	struct stu s1 = { "zhangsan",20,"男","1343344" };//默认顺序初始化
	struct stu s2 = {.age=30,.name="wangwu",.sex="nv",.id="2343343"};//指定顺序初始化
	printf("%s %d %s %s\n", s1.name, s1.age, s1.sex, s1.id);
	printf("%s %d %s %s\n", s2.name, s2.age, s2.sex, s2.id);
	struct S1 s3 = { 0 };
	struct S2 s4 = { 0 };
	printf("%zd\n", sizeof(struct S1));//8
	printf("%zd\n", sizeof(struct S2));//12
	printf("%zd\n", sizeof(struct S3));//16
	printf("%zd\n", sizeof(struct S4));//32
	printf("%zd\n", offsetof(struct S1,c1));//0
	printf("%zd\n", offsetof(struct S1, c2));//1
	printf("%zd\n", offsetof(struct S1, i));//4
	print1(s); //传结构体
	print2(&s); //传地址
	struct A s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	printf("%zd\n", sizeof(s));
	//位段的⼏个成员共有同⼀个字节，这样有些成员的起始位置并不是某个字节的起始位置，那么这些位置处是没有地址的。
	//内存中每个字节分配⼀个地址，⼀个字节内部的bit位是没有地址的。
	struct A sa = { 0 };
	//scanf("%d", &sa.b);//这是错误的
    //正确的⽰范
	int b = 0;
	scanf("%d", &b);
	sa.b = b;

	return 0;
}